package cluster

import (
	"encoding/json"
	"fmt"
	"time"

	myactor "gitee.com/nggs/actor"
	"gitee.com/nggs/microservice/rpc"
	"gitee.com/nggs/microservice/service"
	"gitee.com/nggs/protoactor-go/actor"
)

var Protocol = rpc.New(nil)

func (e Error) IsNil() bool {
	return e.Code == EC_Success
}

func (e Error) Error() string {
	if e.Message != "" {
		return e.Message
	}
	switch e.Code {
	case EC_Success:
		return "success"
	case EC_Fail:
		return "fail"
	case EC_ServiceGroupNotExist:
		return "service group not exist"
	case EC_ServiceInfoNotExist:
		return "service info not exist"
	case EC_MarshalInstanceFail:
		return fmt.Sprintf("marshal instance fail, %s", e.Message)
	case EC_PutInstanceToEtcdFail:
		return fmt.Sprintf("put instance to etcd fail, %s", e.Message)
	case EC_ServiceInstanceAlreadyExist:
		return "service instance already exist"
	case EC_PutPayloadToEtcdFail:
		return fmt.Sprintf("put payload to etcd fail, %s", e.Message)
	}
	return ""
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (si *ServiceInstance) IsEmpty() bool {
	return si.PID == nil || si.PID.Address == "" || si.PID.Id == ""
}

func (si *ServiceInstance) CopyFrom(instance *service.Instance) *ServiceInstance {
	if instance.PID.Address != "" && instance.PID.Id != "" {
		si.PID = actor.NewPID(instance.PID.Address, instance.PID.Id)
	}
	si.PprofAddr = instance.PprofAddr
	if len(instance.Extra) > 0 {
		si.Extra, _ = json.Marshal(instance.Extra)
	}
	return si
}

func (si *ServiceInstance) DumpTo(instance *service.Instance) *ServiceInstance {
	if si.PID != nil {
		instance.PID.Address = si.PID.Address
		instance.PID.Id = si.PID.Id
	}
	instance.PprofAddr = si.PprofAddr
	if len(si.Extra) > 0 {
		err := json.Unmarshal([]byte(si.Extra), &instance.Extra)
		if err != nil {
			// todo
		}
	}
	return si
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (si *ServiceInfo) IsEmpty() bool {
	return len(si.ConfigData) == 0
}

func (si *ServiceInfo) CopyFrom(info *service.Info) *ServiceInfo {
	si.ID = int32(info.Config.ID())
	if len(info.ConfigData) > 0 {
		si.ConfigData = make([]byte, len(info.ConfigData))
		copy(si.ConfigData, info.ConfigData)
	}
	si.Payload = int32(info.Payload)
	if info.Instance != nil && !info.Instance.IsEmpty() {
		si.Instance.CopyFrom(info.Instance)
	}
	return si
}

func (si *ServiceInfo) DumpTo(info *service.Info) *ServiceInfo {
	info.Config.SetID(int(si.ID))
	if len(si.ConfigData) > 0 {
		info.ConfigData = make([]byte, len(si.ConfigData))
		copy(info.ConfigData, si.ConfigData)
	}
	info.Payload = int(si.Payload)
	if si.Instance != nil && !si.Instance.IsEmpty() {
		if info.Instance == nil {
			info.Instance = service.NewInstance()
		}
		si.Instance.DumpTo(info.Instance)
	}
	return si
}

type C2S_AddWatchCallback struct {
	cb WatchCallback
}

type S2C_AddWatchCallback struct {
	err error
}

func Request_S2C_AddWatchCallback(pid *actor.PID, send *C2S_AddWatchCallback) (*S2C_AddWatchCallback, error) {
	return Request_S2C_AddWatchCallback_T(pid, send, 5*time.Second)
}

func Request_S2C_AddWatchCallback_T(pid *actor.PID, send *C2S_AddWatchCallback, timeout time.Duration) (*S2C_AddWatchCallback, error) {
	if pid == nil {
		return nil, fmt.Errorf("pid is nil")
	}
	f := myactor.RootContext.RequestFuture(pid, send, timeout)
	iRecv, err := f.Result()
	if err != nil {
		return nil, err
	}
	recv, ok := iRecv.(*S2C_AddWatchCallback)
	if !ok {
		return nil, fmt.Errorf("recv %#v is not S2C_AddWatchCallback", recv)
	}
	return recv, nil
}
